package br.mikhas.util;

import java.io.PrintWriter;

public final class ExceptionUtils {
	private ExceptionUtils() {

	}

	/**
	 * Gets the given exception the root cause
	 * 
	 * @param exception
	 *            The exception
	 * @return the exception root cause
	 */
	public static Throwable getRootCause(Exception exception) {
		Assert.notNull(exception);
		Throwable t = exception;
		while (t.getCause() != null)
			t = t.getCause();
		return t;
	}

	/**
	 * Wraps a checked {@link Exception} into a {@link RuntimeException}
	 * 
	 * @param exception
	 *            the exception to be wrapped
	 * @return an {@link Exception} wrapper
	 */
	public static RuntimeException wrapChecked(Exception exception) {
		if (exception instanceof RuntimeException)
			return (RuntimeException) exception;
		return new CheckedExceptionWrapper(exception);
	}

	/**
	 * Wraps and throws a checked {@link Exception} as a
	 * {@link RuntimeException}
	 * 
	 * @param exception
	 *            the exception to be wrapped
	 * @throws RuntimeException
	 *             throws the wrapped exception
	 */
	public static void throwWrapped(Exception exception)
			throws RuntimeException {
		if (exception instanceof RuntimeException)
			throw (RuntimeException) exception;
		throw new CheckedExceptionWrapper(exception);
	}

	/**
	 * Wraps an checked exception.
	 * <p>
	 * This wrapper hides the checked exception attribute as a
	 * {@link RuntimeException}.
	 * <p>
	 * Any method called from this wrapper will be called on the wrapped
	 * exception
	 * 
	 * @author Mikhail Domanoski
	 * 
	 */
	private static class CheckedExceptionWrapper extends RuntimeException {

		private static final long serialVersionUID = -5848317527421425295L;
		/**
		 * The wrapped exception
		 */
		private final Exception wrapped;

		private CheckedExceptionWrapper(Exception exception) {
			Assert.notNull(exception);
			wrapped = exception;
			this.setStackTrace(wrapped.getStackTrace());
		}

		@Override
		public boolean equals(Object obj) {
			return wrapped.equals(obj);
		}

		@Override
		public Throwable getCause() {
			return wrapped.getCause();
		}

		@Override
		public String getLocalizedMessage() {
			return wrapped.getLocalizedMessage();
		}

		@Override
		public StackTraceElement[] getStackTrace() {
			return wrapped.getStackTrace();
		}

		@Override
		public String getMessage() {
			return wrapped.getMessage();
		}

		public synchronized Throwable fillInStackTrace() {
			if (wrapped == null)
				return super.fillInStackTrace();
			else
				return wrapped.fillInStackTrace();
		}

		@Override
		public int hashCode() {
			return wrapped.hashCode();
		}

		@Override
		public void printStackTrace(PrintWriter s) {
			wrapped.printStackTrace(s);
		}

		@Override
		public synchronized Throwable initCause(Throwable cause) {
			return wrapped.initCause(cause);
		}

		public void printStackTrace(java.io.PrintStream s) {
			wrapped.printStackTrace(s);
		}

		@Override
		public void setStackTrace(StackTraceElement[] stackTrace) {
			wrapped.setStackTrace(stackTrace);
		}

		@Override
		public String toString() {
			return wrapped.toString();
		}
	}
}
